__version__ = "$Id$"
import os
import threading
import random
import urllib2
import cherrypy
from mako.template import Template
from mako.lookup import TemplateLookup
from urllib import quote_plus
from web.webbase import *
from web.position import positions,positionspercents
from web.asset import asset
from web.account import accounts,accounttotals
from web.transaction import transactions
from web.checking import checking
from cherrypy.lib.static import serve_file
from optparse import OptionParser
import logging

#Crazy line needed for OS X 10.6
urllib2.install_opener(urllib2.build_opener())
current_dir = os.path.dirname(os.path.abspath(__file__))

class Main(object):
    def __init__(self):
        self.lock=threading.Lock()
        self.msgs={}
        self.lastFetch=None
        
    def jmsg(self,func,**msg):
        """msgs communicate url parameters between the method that fetches the web page
and fetch_json that generates the content for that web page:
the url parameters are kept in a dictonary (msg) that also hold the key 'func' which gives
a pointer to a function that accepts msg as single parameter and returns the JSON response.
each msg is held with a random key which is also givn to the web page.
the web page using its Mako template pulls the JSON information using fetch_json?msgkey=<msgkey>"""
        msg['func']=func
        # instead of using a random msgkey str(random.getrandbits(30))
        # use a deterministic hash of msg which is assumed to have values that are eitehr str or [str]
        msgkey=()
        keys=msg.keys()
        keys.sort()
        for key in keys:
            value = msg[key]
            if isinstance(value,list):
                value.sort()
                value=tuple(value)
            msgkey+=(key,value)
        msgkey=str(abs(hash(msgkey)))
        self.lock.acquire()
        if msgkey not in self.msgs:
            self.msgs[msgkey]=msg
        self.lock.release()
        return msgkey
    
    @cherrypy.expose
    def fetch_json(self,jmsg):
        self.lock.acquire()
        if jmsg=='last':
            jmsg=self.lastFetch
        assert(jmsg in self.msgs)
        msg=self.msgs[jmsg]
        #del self.msgs[jmsg] # each msg can be used once
        if 'items' in msg:
            items=msg['items']
        else:
            func=msg['func']
            args=msg.copy()
            del args['func']
            for k,v in args.iteritems():
                if isinstance(v,basestring):
                    args[k]=set([v]) if v else set()
                else:
                    args[k]=set(v)
            items = func(**args)
            msg['items']=items
        self.lastFetch=jmsg
        self.lock.release()
        return dumps({'items':items})
    @cherrypy.expose
    def index(self):
        url=cherrypy.config.get('sesame.url')
        repository=cherrypy.config.get('sesame.repository')
        url+='/openrdf-workbench/repositories/'+repository
        t=lookup.get_template('index.html')
        return t.render(url=url,config=cherrypy.config)
    @cherrypy.expose
    def transactions(self,DTSTART,DTEND,ACCTID):
        t=lookup.get_template('transactions.html')
        return t.render(jmsg=self.jmsg(transactions,acctids=ACCTID,
                                       dtstart=DTSTART,dtend=DTEND),
                        config=cherrypy.config)
    @cherrypy.expose
    def checking(self,DTSTART,DTEND,ACCTID):
        t=lookup.get_template('checking.html')
        return t.render(jmsg=self.jmsg(checking,acctids=ACCTID,
                                       dtstart=DTSTART,dtend=DTEND),
                        config=cherrypy.config)
    @cherrypy.expose
    def positions(self,DTSTART,DTEND,ACCTID):
        t=lookup.get_template('positions.html')
        return t.render(jmsg=self.jmsg(positions,acctids=ACCTID,
                                       dtstart=DTSTART,dtend=DTEND),
                        config=cherrypy.config)
    @cherrypy.expose
    def positionspercents(self,DTSTART,DTEND,ACCTID):
        t=lookup.get_template('positionspercents.html')
        return t.render(jmsg=self.jmsg(positionspercents,acctids=ACCTID,
                                       dtstart=DTSTART,dtend=DTEND),
                        config=cherrypy.config)
    @cherrypy.expose
    def accounttotals(self,DTSTART,DTEND,ACCTID):
        t=lookup.get_template('accounttotals.html')
        return t.render(jmsg=self.jmsg(accounttotals,acctids=ACCTID,
                                       dtstart=DTSTART,dtend=DTEND),
                        config=cherrypy.config)
    @cherrypy.expose
    def accounts(self):
        t=lookup.get_template('accounts.html')
        return t.render(jmsg=self.jmsg(accounts),config=cherrypy.config)
    @cherrypy.expose
    def asset(self,UNIQUEID):
        t=lookup.get_template('asset.html')
        return t.render(jmsg=self.jmsg(asset,uniqueids=UNIQUEID),
                        config=cherrypy.config)
    @cherrypy.expose
    def item(self):
        t=lookup.get_template('item.html')
        return t.render(jmsg='last',config=cherrypy.config)
    
def main():
    parser = OptionParser(usage="usage: %prog [options]",
                          description="Webserver front-end for 3account",
                          version=__version__)
    parser.add_option("-v", "--verbose",
                      action="store_true", dest="verbose")
    (options, args) = parser.parse_args()    
    if options.verbose:
        logging.basicConfig(level=logging.INFO)
        cherrypy.log.error_log.setLevel(logging.INFO)
        cherrypy.log.access_log.setLevel(logging.INFO)
    else:
        logging.basicConfig(level=logging.WARNING)
        cherrypy.log.error_log.setLevel(logging.WARNING)
        cherrypy.log.access_log.setLevel(logging.WARNING)

    conf = {'/css': {'tools.staticdir.on': True,
                     'tools.staticdir.dir': os.path.join(current_dir, 'static','css')},
            '/js': {'tools.staticdir.on': True,
                    'tools.staticdir.dir':  os.path.join(current_dir, 'static','js')},
            '/__history__.html': {'tools.staticfile.on': True, # for Back and Forward buttons in the browser works as undo and redo
                                  'tools.staticfile.filename':
                                  os.path.join(current_dir, 'static','__history__.html')},
            }
    cherrypy.quickstart(Main(),'/',config=conf)

if __name__ == '__main__':
    main()
